package pers.hl.library.auth;

import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 内存存储缓存.
 * 实现基于内存的缓存存储实现， 该缓存具有时效性，会基于定时任务{@link #executorService}定时清理内存数据.
 * 如果设置添加内存不具有时效性，数据过多，会导致内存溢出风险.
 */
public class TimeoutMemoryCacheHelper {
    private static TimeoutMemoryCacheHelper instance;
    private static final long serialVersionUID = 8094709764231298381L;
    private ScheduledExecutorService executorService;
    /*** 两次任务之间执行间隔时间，默认30分钟 */
    private final long delay = 30L * 60L;
    /*** 初始化任务延迟10分钟执行 */
    private final long initialDelay = 10L * 60L;

    public synchronized static TimeoutMemoryCacheHelper getInstance() {
        if (instance == null) {
            instance = new TimeoutMemoryCacheHelper();
        }
        return instance;
    }

    private TimeoutMemoryCacheHelper() {
        createTask();
    }

    private void createTask() {
        executorService = Executors.newScheduledThreadPool(1);
        executorService.scheduleWithFixedDelay(new Task(), initialDelay, delay, TimeUnit.SECONDS);
    }

    private ConcurrentHashMap<String, Object> memoryStorage = new ConcurrentHashMap<String, Object>();

    private ConcurrentHashMap<String, Object> getCacheMap() {
        return this.memoryStorage;
    }

    public boolean update(String key, Object value, long timeout) {
        Cache cache = createCache(timeout, value);
        getCacheMap().put(key, cache);
        return true;
    }

    protected Cache createCache(long timeout, Object value) {
        Cache cache = null;
        if (value instanceof Cache) {
            cache = (Cache) value;
            cache.lastRequest();
        } else {
            cache = new Cache(timeout, value);
        }
        return cache;
    }

    public boolean add(String key, Object value, long timeout) {
        Cache cache = createCache(timeout, value);
        getCacheMap().put(key, cache);
        return true;
    }

    public boolean delete(String key) {
        getCacheMap().remove(key);
        return true;
    }

    public boolean exists(String key) {
        Object obj = getValue(key);
        return obj != null;
    }

    public Object get(String key) {
        Object obj = getValue(key);
        return obj;
    }

    protected Object getValue(String key) {
        Cache value = (Cache) memoryStorage.get(key);
        if (isNull(value)) {
            return null;
        }
        if (value.isTimeout()) {
            delete(key);
            return null;
        }
        return value.getValue();
    }

    public <T> T get(String key, Class<T> clazz) {
        Object obj = getValue(key);
        return toObject(obj, clazz);
    }

    protected boolean isNull(Cache value) {
        return value == null || value.isTimeout();
    }

    public <T> T toObject(Object value, Class<T> clazz) {
        if (value == null) {
            return null;
        }
        if (value.getClass().isAssignableFrom(clazz)) {
            return (T) value;
        }
        throw new ClassCastException("Class [" + value.getClass().getCanonicalName() + "] can't cast to [" + clazz.getCanonicalName() + "]!");
    }

    public void destory() {
        executorService.shutdownNow();
        executorService = null;
        this.memoryStorage.clear();
        this.memoryStorage = null;
    }

    private class Task implements Runnable {
        @Override
        public void run() {
            Map<String, Object> cacheMap = getCacheMap();
            for (Iterator<Map.Entry<String, Object>> iterator = cacheMap.entrySet().iterator(); iterator.hasNext(); ) {
                Map.Entry entry = iterator.next();
                String key = (String) entry.getKey();
                Object value = entry.getValue();
                if (value instanceof Cache && ((Cache) value).isTimeout()) {
                    System.out.println("移除过期token" + key);
                    cacheMap.remove(key);
                }
            }
        }
    }

    /*** 缓存对象 */
    private class Cache implements Serializable {
        private Object value;
        private long timeout;
        private long cacheTime = System.currentTimeMillis();

        public Cache(long timeout, Object value) {
            this.timeout = timeout;
            this.value = value;
        }

        public boolean isTimeout() { // 永不过期
            if (this.timeout <= 0) {
                return false;
            }// 当前系统时间减去（缓存时间+超时时间）大于0表示当前已失效
            return System.currentTimeMillis() - (cacheTime + this.timeout * 1000) > 0;
        }

        public Object getValue() {
            return value;
        }

        /*** 最后请求时间，延长有效期 */
        public void lastRequest() {
            this.cacheTime = System.currentTimeMillis();
        }
    }
}
